/*
剑指 Offer II 011. 0 和 1 个数相同的子数组
给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组，并返回该子数组的长度。

 

示例 1:

输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。
示例 2:

输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量 0 和 1 的最长连续子数组。
 

提示：

1 <= nums.length <= 105
nums[i] 不是 0 就是 1
 

注意：本题与主站 525 题相同： https://leetcode-cn.com/problems/contiguous-array/
*/

#include<vector>
#include<unordered_map>

using namespace std;
class Solution {
public:
    // n ^ 2解法，超出时间限制
    int findMaxLength(vector<int>& nums) {
        int n = nums.size();
        vector<int> sums(n+1);
        for(int i = 1; i <= n; ++i)
        {
            sums[i] = sums[i-1] + nums[i-1];
        }

        int result = 0;
        for(int i = 0; i < n;++i)
        {
            for(int j = i + 1;j <= n;++j)
            {
                int length = j - i;
                if(length % 2 == 0 && sums[j] - sums[i] == length / 2)
                {
                    result = result > length ? result:length;
                }
            }
        }

        return result;
    }

    //由于「0 和 1 的数量相同」等价于「1 的数量减去 0 的数量等于 0」，我们可以将数组中的 0 视作 -1，
    //则原问题转换成「求最长的连续子数组，其元素和为 0」。
    int findMaxLength(vector<int>& nums)
    {
        int n = nums.size();

        int result = 0;
        int sum = 0;
        //record sum first show up index
        unordered_map<int,int> prefixMap;
        //initial sum == 0 the first show index to -1
        //i - preIndex no need to add 1
        prefixMap[0] = -1;
        for(int i = 0; i < n;++i)
        {
          if(nums[i] == 1)
          {
              ++sum;
          }
          else
          {
              --sum;
          }

          if(prefixMap.find(sum) != prefixMap.end())
          {
              int preIndex = prefixMap[sum];
              //prefixMap记录sum的索引,差值即为区间元素个数
              int length = i - preIndex;
              result = result > length ? result : length;
          }
          else
          {
              //记录和的第一次索引和为sum时的索引
              prefixMap[sum] = i;
          }
        }

        return result;
    }
};